#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "mcp_capability/ToolsMethodBuilder.h"
#include "mcp_capability/CapabilityMethod.h"
#include <QJsonObject>
#include <QJsonArray>
#include <QVector>

using namespace testing;

class ToolsMethodBuilderTest : public Test
{
protected:
    void SetUp() override
    {
        builder = new ToolsMethodBuilder();
    }
    
    void TearDown() override
    {
        delete builder;
    }
    
    ToolsMethodBuilder* builder;
};

TEST_F(ToolsMethodBuilderTest, ShouldCreateEmptyBuilder)
{
    ASSERT_NE(builder, nullptr);
}

TEST_F(ToolsMethodBuilderTest, ShouldSetNameAndReturnReference)
{
    IMethodBuilder& result = builder->setName("testTool");
    ASSERT_EQ(&result, builder);
}

TEST_F(ToolsMethodBuilderTest, ShouldSetTitleAndReturnReference)
{
    IMethodBuilder& result = builder->setTitle("Test Tool");
    ASSERT_EQ(&result, builder);
}

TEST_F(ToolsMethodBuilderTest, ShouldSetDescriptionAndReturnReference)
{
    IMethodBuilder& result = builder->setDescription("A test tool description");
    ASSERT_EQ(&result, builder);
}

TEST_F(ToolsMethodBuilderTest, ShouldAcceptInputSchemaParameter)
{
    QJsonObject schema;
    schema["type"] = "object";
    
    QString errorMsg;
    IMethodBuilder& result = builder->setParameters("inputSchema", schema, &errorMsg);
    
    ASSERT_EQ(&result, builder);
    ASSERT_TRUE(errorMsg.isEmpty());
}

TEST_F(ToolsMethodBuilderTest, ShouldAcceptOutputSchemaParameter)
{
    QJsonObject schema;
    schema["type"] = "object";
    
    QString errorMsg;
    IMethodBuilder& result = builder->setParameters("outputSchema", schema, &errorMsg);
    
    ASSERT_EQ(&result, builder);
    ASSERT_TRUE(errorMsg.isEmpty());
}

TEST_F(ToolsMethodBuilderTest, ShouldRejectUnknownParameter)
{
    QJsonObject schema;
    schema["type"] = "object";
    
    QString errorMsg;
    IMethodBuilder& result = builder->setParameters("unknownParam", schema, &errorMsg);
    
    ASSERT_EQ(&result, builder);
    ASSERT_FALSE(errorMsg.isEmpty());
    ASSERT_EQ(errorMsg, "Unknown parameter name: unknownParam\n");
}

TEST_F(ToolsMethodBuilderTest, ShouldBuildCompleteMethod)
{
    // Setup complete method
    builder->setName("testTool")
           .setDescription("Test tool description")
           .setTitle("Test Tool");
    
    QJsonObject inputSchema;
    inputSchema["type"] = "object";
    
    builder->setParameters("inputSchema", inputSchema);
    
    // Create a simple task
    auto task = createTask([](const QJsonObject&) -> QJsonObject {
        QJsonObject result;
        result["result"] = "success";
        return result;
    });
    builder->setMethodTask(task);
    
    QString errorMsg;
    CapabilityMethod result = builder->build(&errorMsg);
    
    ASSERT_TRUE(errorMsg.isEmpty());
    ASSERT_FALSE(result.describe.isEmpty());
    
    ASSERT_EQ(result.describe["name"].toString(), "testTool");
    ASSERT_EQ(result.describe["title"].toString(), "Test Tool");
    ASSERT_EQ(result.describe["description"].toString(), "Test tool description");
    ASSERT_TRUE(result.describe.contains("inputSchema"));
    ASSERT_NE(result.task, nullptr);
}

TEST_F(ToolsMethodBuilderTest, ShouldFailBuildWhenNameMissing)
{
    builder->setDescription("Test tool description");
    
    QString errorMsg;
    CapabilityMethod result = builder->build(&errorMsg);
    
    ASSERT_FALSE(errorMsg.isEmpty());
    ASSERT_TRUE(result.describe.isEmpty());
}

TEST_F(ToolsMethodBuilderTest, ShouldFailBuildWhenDescriptionMissing)
{
    builder->setName("testTool");
    
    QString errorMsg;
    CapabilityMethod result = builder->build(&errorMsg);
    
    ASSERT_FALSE(errorMsg.isEmpty());
    ASSERT_TRUE(result.describe.isEmpty());
}

TEST_F(ToolsMethodBuilderTest, ShouldClearAllData)
{
    // Setup some data
    builder->setName("testTool")
           .setDescription("Test description")
           .setTitle("Test Title");
    
    QJsonObject schema;
    schema["type"] = "object";
    builder->setParameters("inputSchema", schema);
    
    // Clear and verify
    builder->clear();
    
    QString errorMsg;
    CapabilityMethod result = builder->build(&errorMsg);
    
    ASSERT_FALSE(errorMsg.isEmpty());
    ASSERT_TRUE(result.describe.isEmpty());
}

TEST_F(ToolsMethodBuilderTest, ShouldGenerateSchemaWithProperties)
{
    ToolsMethodBuilder::Property prop1("name", "string", "The name of the tool", true);
    ToolsMethodBuilder::Property prop2("enabled", "boolean", "Whether the tool is enabled", false);
    
    QVector<ToolsMethodBuilder::Property> properties;
    properties.append(prop1);
    properties.append(prop2);
    
    QJsonObject schema = builder->genSchema(properties);
    
    ASSERT_EQ(schema["type"].toString(), "object");
    ASSERT_TRUE(schema.contains("properties"));
    ASSERT_TRUE(schema.contains("required"));
    
    QJsonObject props = schema["properties"].toObject();
    ASSERT_TRUE(props.contains("name"));
    ASSERT_TRUE(props.contains("enabled"));
    
    QJsonArray required = schema["required"].toArray();
    ASSERT_EQ(required.size(), 1);
    ASSERT_EQ(required[0].toString(), "name");
}

TEST_F(ToolsMethodBuilderTest, ShouldSupportChaining)
{
    // Create a simple task
    auto task = createTask([](const QJsonObject&) -> QJsonObject {
        QJsonObject result;
        result["result"] = "chained success";
        return result;
    });
    
    QString errorMsg;
    CapabilityMethod result = builder->setName("chainedTool")
                               .setDescription("Chained tool")
                               .setTitle("Chained")
                               .setMethodTask(task)
                               .build(&errorMsg);
    
    ASSERT_TRUE(errorMsg.isEmpty());
    ASSERT_FALSE(result.describe.isEmpty());
    ASSERT_EQ(result.describe["name"].toString(), "chainedTool");
    ASSERT_EQ(result.describe["description"].toString(), "Chained tool");
    ASSERT_EQ(result.describe["title"].toString(), "Chained");
    ASSERT_NE(result.task, nullptr);
}
